import asyncio
import time

import aiohttp
from tqdm import tqdm
import os
import threading


class DownVideoPool:
    def __init__(self):
        self.urls = []
        self.dv_classes = []

    def append_urls(self, urls):
        for i in urls:
            self.urls.append([i, self.process_name(i)])

    @staticmethod
    def process_name(url):
        name = url.split("/")[-1].replace("?", "_")
        return name

    def start(self):
        for _ in self.urls:
            self.dv_classes.append(DownVideo())

        for url, dv in zip(self.urls, self.dv_classes):
            threading.Thread(target=dv.main, args=[url[0], f"./{url[1]}"]).start()


class DownVideo:
    def __init__(self):
        # 请求头
        self.__headers = {
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
            "cookie": "Hm_lvt_50027a9c88cdde04a70f5272a88a10fa=1682781321; Hm_lpvt_50027a9c88cdde04a70f5272a88a10fa=1682781321",
            "origin": "https://music.liuzhijin.cn",
            "sec-ch-ua": '"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"',
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "Windows",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.64",
            "x-requested-with": "XMLHttpRequest",
        }

    @staticmethod
    async def __down_video(bar, queue):
        """
        :param bar: 进度条
        """
        while not queue.empty():
            task = await queue.get()
            session, path, video_url, start, headers = task[0], task[1], task[2], task[3], task[4]
            async with session.get(video_url, headers=headers) as resp:
                with open(path, 'rb+') as f:
                    # 写入位置，指针移到指定位置
                    f.seek(start)
                    async for b in resp.content.iter_chunked(1024 * 1024):
                        f.write(b)
                        # 更新进度条，每次请求得到的长度
                        bar.update(len(b))

    async def __start_async(self, video_url, path, count=16):
        """
        :param video_url:视频地址
        :param path: 视频保存位置
        :param count:协程数量
        """
        _path_, name = os.path.split(path)
        # 判断目录是否存在
        if not os.path.exists(_path_):
            print('创建文件夹', _path_)
            os.mkdir(_path_)
        async with aiohttp.ClientSession() as session:
            # 文件长度
            content_length = await self.__get_length(session, path, video_url=video_url)
            print('content_length :', content_length)

            # 将文件按大小分解为任务队列
            queue = asyncio.Queue()
            # [quque.put_nowait()]
            # 每个区块1M大小
            size = 1024 * 10240
            amount = content_length // size or 1
            for i in range(amount):
                start = i * size
                if i == amount - 1:
                    end = content_length
                else:
                    end = start + size
                if start > 0:
                    start += 1
                # 设置请求视频位置
                headers = {
                    'Range': f'bytes={start}-{end}'
                }
                # 合并请求头
                headers.update(self.__headers)
                queue.put_nowait([session, path, video_url, start, headers])

            with tqdm(total=content_length, unit='', desc=f'下载：{name}', unit_divisor=1024, ascii=True,
                      unit_scale=True) as bar:
                # 开始协程
                await asyncio.wait([self.__down_video(bar, queue) for _ in range(count)])

    async def __get_length(self, session, path, video_url):
        """创建和视频大小一样文件，并返回视频长度"""
        async with session.get(video_url, headers=self.__headers) as resp:
            # 获取视频长度
            le = int(resp.headers['Content-Length'])
            f = open(path, 'wb')
            f.truncate(le)
            f.close()
            return le

    def main(self, video_url, path, count=16):
        """
        :param video_url: 视频地址
        :param path: 保存地址
        :param count: 协程数量
        """
        asyncio.run(self.__start_async(video_url, path, count))


if __name__ == '__main__':
    # 视频真实地址
    video_url_ = 'http://music.163.com/song/media/outer/url?id=1451479710.mp3'
    # 文件路径
    path_ = './3.mp3'
    dv = DownVideo()
    # 开始
    dv.main(video_url_, path_)
    # dvp = DownVideoPool()
    # dvp.append_urls(
    #     ["http://music.163.com/song/media/outer/url?id=1860964106.mp3",
    #      "http://music.163.com/song/media/outer/url?id=1440302314.mp3",
    #      "http://music.163.com/song/media/outer/url?id=475264125.mp3",
    #      "http://music.163.com/song/media/outer/url?id=471566609.mp3",
    #      "http://music.163.com/song/media/outer/url?id=1397992064.mp3",
    #      "http://music.163.com/song/media/outer/url?id=1490544384.mp3",
    #      "http://music.163.com/song/media/outer/url?id=2022377680.mp3",
    #      "http://music.163.com/song/media/outer/url?id=4010876.mp3",
    #      "http://music.163.com/song/media/outer/url?id=1422556788.mp3",
    #      "http://music.163.com/song/media/outer/url?id=1397992069.mp3", ]
    # )
    # time.sleep(0.5)
    # dvp.start()
